Skip to content

hardening(dap): bound DapFrameDecoder frames and make accumulation linear - #413

Merged
debugmcpdev merged 1 commit into
mainfrom
fix/402-frame-decoder-bounds
Aug 22, 2026
Merged

hardening(dap): bound DapFrameDecoder frames and make accumulation linear#413
debugmcpdev merged 1 commit into
mainfrom
fix/402-frame-decoder-bounds

Conversation

@debugmcpdev

Copy link
Copy Markdown
Collaborator

Fixes #402

DapFrameDecoder accepted any finite positive Content-Length — a peer advertising a huge frame made the buffer grow until OOM (a buggy/compromised adapter can OOM the proxy worker; expose_session mirror clients are loopback + token-gated but defense-in-depth says the decoder itself must cap). Growth was unbounded even before any header: a garbage stream with no \r\n\r\n buffered forever. And push() did Buffer.concat per chunk — O(N²) when a large frame arrives in small chunks.

Change

  • maxContentLength option (default 64 MB, matching the repo's maxBuffer precedent; env-overridable via DAP_MAX_FRAME_BYTES). Over-cap advertisements → new error context 'overflow' + buffer discard, same recovery contract as 'header'.
  • Header-search bound: >16 KB with no header separator → 'overflow' + discard (headers are a handful of short lines).
  • Linear accumulation: header bytes live in a small bounded buffer; body bytes accumulate as a chunk list with a byte count, concatenated once per completed frame. No more per-chunk concat of the whole backlog.
  • Consumers: MinimalDapClient treats 'overflow' like 'header' (warn + continue, decoder already discarded); DapMirrorServer closes the mirror connection on 'overflow' as it does on 'header' — a client advertising an over-cap frame is equally untrustworthy.

Tests (property suite extended; new cases watched fail first)

  • Over-cap Content-Length → 'overflow' reported, payload discarded, decoder recovers on the next push.
  • Exactly-at-cap frame decodes (boundary).
  • 20 KB of header-less garbage → 'overflow' + recovery (previously buffered forever).
  • 256 KB frame delivered in 1 KB chunks reassembles (the O(N²) shape, now linear).
  • The existing chunking-invariance/reassembly fast-check properties and malformed-input recovery cases all still pass; two minimal-dap tests that reached into the decoder's old private rawData now assert the renamed internals.
  • Full npm test green, npm run lint clean. DAP_MAX_FRAME_BYTES documented in the setup guide.

🤖 Generated with Claude Code

…near

The decoder accepted any finite positive Content-Length (a hostile or
buggy peer could OOM the proxy worker / mirror connection), buffered
header-less garbage without bound, and Buffer.concat'ed per chunk —
O(N^2) for large frames arriving in small pieces (issue #402).

- maxContentLength option, default 64 MB (DAP_MAX_FRAME_BYTES override);
  over-cap advertisements report a new 'overflow' error context and
  discard the buffer, same recovery contract as 'header'
- header-search accumulation bounded at 16 KB
- body bytes accumulate as a chunk list concatenated once per frame
- MinimalDapClient warns and continues on overflow; DapMirrorServer
  closes the mirror connection (as it does for corrupt headers)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@debugmcpdev
debugmcpdev merged commit 0b8e2a9 into main Aug 22, 2026
9 checks passed
@debugmcpdev
debugmcpdev deleted the fix/402-frame-decoder-bounds branch August 22, 2026 04:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

hardening(dap): DapFrameDecoder accepts unbounded Content-Length and concats per chunk

2 participants